home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 3 / Cream of the Crop 3.iso / comm / wnos5src.zip / PC.C < prev    next >
Text File  |  1993-10-18  |  21KB  |  898 lines

  1. /* OS- and machine-dependent stuff for IBM-PC running MS-DOS and Turbo-C */
  2. #include <stdio.h>
  3. #include <dos.h>
  4. #include <conio.h>
  5. #include <dir.h>
  6. #include <io.h>
  7. #include <sys/stat.h>
  8. #include <string.h>
  9. #include <process.h>
  10. #include <fcntl.h>
  11. #include <alloc.h>
  12. #include <stdarg.h>
  13. #include <bios.h>
  14. #include "global.h"
  15. #include "config.h"
  16. #include "mbuf.h"
  17. #include "socket.h"
  18. #include "internet.h"
  19. #include "iface.h"
  20. #include "cmdparse.h"
  21. #include "pc.h"
  22. #include "proc.h"
  23. #include "session.h"
  24. #include "smtp.h"
  25. #ifdef SCC
  26. #include "scc.h"
  27. #endif
  28. #ifdef PACKET
  29. #include "pktdrvr.h"
  30. #endif
  31. #ifdef ASY
  32. #include "asy.h"
  33. #include "n8250.h"
  34. #endif
  35. #include "domain.h"
  36. #include "files.h"
  37.  
  38. #define DEL             0x7f
  39.  
  40. extern struct proc *Display;
  41. extern int getproc __ARGS((void));
  42.  
  43. int Tick = 0;
  44. static int32 Starttime;
  45. int32 Clock;
  46.  
  47. /* This flag is set by setirq() if IRQ 8-15 is used, indicating
  48.  * that the machine is a PC/AT with a second 8259 interrupt controller.
  49.  * If this flag is set, the interrupt return code in pcgen.asm will
  50.  * send an End of Interrupt command to the second 8259 as well as the
  51.  * first.
  52.  */
  53. int Isat = 0;
  54.  
  55. static int saved_break;
  56.  
  57. #define KBSIZE  256             /* Keyboard input buffer */
  58.  
  59. static struct {
  60.     char buf[KBSIZE];
  61.     char *wp;
  62.     char *rp;
  63.     int cnt;
  64. } Keyboard;
  65.  
  66. static int Swap = FALSE;
  67.  
  68. extern int do_spawn __ARGS((int swapping,char *execfname,char *cmdtail,unsigned envlen,char *envp));
  69. extern int prep_swap __ARGS((unsigned method,char *swapfname));
  70.  
  71. /* Directly read BIOS count of time ticks. This is used instead of
  72.  * calling biostime(0,0L). The latter calls BIOS INT 1A, AH=0,
  73.  * which resets the midnight overflow flag, losing days on the clock.
  74.  */
  75. static long near
  76. bioscnt(void)
  77. {
  78.     int i_state = dirps();
  79.  
  80.     long rval = *(long far *)MK_FP(0x40,0x6c);
  81.  
  82.     restore(i_state);
  83.  
  84.     return rval;
  85. }
  86.  
  87. static int err = 0;
  88.  
  89. /* define the error messages for trapping disk problems - D.Crompton
  90.  * ported into WNOS DB3FL.910407
  91.  */
  92. static int
  93. handler(int errval,int ax,int bp,int si)
  94. {
  95.     if(ax >= 0 && err == 0) {
  96.         char *crit_err_msg[] = {
  97.             "Write protect",
  98.             "Unknown unit",
  99.             "Not ready",
  100.             "Unknown command",
  101.             "Data error (CRC)",
  102.             "Bad request",
  103.             "Seek error",
  104.             "Unknown media type",
  105.             "Sector not found",
  106.             "Printer out of paper",
  107.             "Write fault",
  108.             "Read fault",
  109.             "General failure",
  110.             "Reserved",
  111.             "Reserved",
  112.             "Invalid disk change"
  113.         };
  114.         printf("%s on drive %c\n",
  115.             crit_err_msg[(_DI & 0x00FF)],(ax & 0x00FF) + 'A');
  116.     }
  117.     if(++err > 2) {
  118.         err = 0;
  119.     }
  120.     hardretn(3);
  121.     return 3;
  122. }
  123.  
  124. /* Called at startup time to set up console I/O, memory heap */
  125. void
  126. ioinit(void)
  127. {
  128.     /* Fail all I/O errors */
  129.     harderr(handler);
  130.  
  131.     /* Save these two file table entries for something more useful */
  132.     Fclose(stdaux);
  133.     Fclose(stdprn);
  134.  
  135.     /* this breaks tab expansion so you must use ANSI or NANSI */
  136.     ioctl(fileno(stdout), 1, (ioctl(fileno(stdout),0) & 0xff) | 0x20);
  137.     saved_break = getcbrk();
  138.     setcbrk(0);
  139.  
  140.     Starttime = bioscnt();
  141.     /* Link timer handler into timer interrupt chain */
  142.     chtimer(btick);
  143.  
  144.     /* Find out what multitasker we're running under, if any */
  145.     chktasker();
  146.  
  147.     if(getproc() > 5)
  148.         Isat = 1;
  149.  
  150.     /* Initialize keyboard queue */
  151.     Keyboard.cnt = 0;
  152.     Keyboard.rp = Keyboard.wp = Keyboard.buf;
  153.  
  154. }
  155.  
  156. /* Called just before exiting to restore console state */
  157. void
  158. iostop(void)
  159. {
  160.     struct iface *ifp, *iftmp = NULLIF;
  161.  
  162.     ioctl(fileno(stdout), 1, (ioctl(fileno(stdout), 0) & 0xff) & ~0x20);
  163.     setcbrk(saved_break);
  164.  
  165.     for(ifp = Ifaces; ifp != NULLIF; ifp = iftmp) {
  166.         iftmp = ifp->next;
  167.         if_detach(ifp);
  168.     }
  169. #ifdef SCC
  170.     sccstop();      /* reset int mask for all scc boards */
  171. #endif
  172.     /* Unlink timer handler from timer chain */
  173.     uchtimer();
  174. }
  175.  
  176. /*-----------------------------------------------------------------------*/
  177. typedef struct {
  178.    struct text_info tr;
  179.    char *sbuf;
  180. } Doslock;
  181.  
  182. static unsigned near dos_prepare(Doslock *dl);
  183. static void near dos_restore(Doslock *dl);
  184.  
  185. static int do_exec __ARGS((char *xfn,char *pars,int spawn,unsigned needed));
  186.  
  187. /* Return codes (only upper byte significant) */
  188.  
  189. #define RC_PREPERR   0x0100
  190. #define RC_NOFILE    0x0200
  191. #define RC_EXECERR   0x0300
  192. #define RC_ENVERR    0x0400
  193. #define RC_SWAPERR   0x0500
  194.  
  195. /* Swap method and option flags */
  196.  
  197. #define USE_EMS      0x01
  198. #define USE_XMS      0x02
  199. #define USE_FILE     0x04
  200. #define EMS_FIRST    0x00
  201. #define XMS_FIRST    0x10
  202. #define HIDE_FILE    0x40
  203. #define NO_PREALLOC  0x100
  204. #define CHECK_NET    0x200
  205.  
  206. #define USE_ALL      (USE_EMS | USE_XMS | USE_FILE)
  207.  
  208. static int near
  209. _spawn(char *env,char *def,int argc,char **argv)
  210. {
  211. Doslock dl;
  212. int i, ret = 0;
  213. char *p, *command = mxallocw(256);
  214.  
  215.     if((p = getenv(env)) == NULL)
  216.         p = def;
  217.  
  218.     dos_prepare(&dl);
  219.  
  220.     if(argc > 1) {
  221.         if(*def == 'C') {
  222.             strcpy(command,"/c");
  223.         }
  224.         for(i = 1; i < argc; i++) {
  225.             strcat(command," ");
  226.             strcat(command,argv[i]);
  227.         }
  228.     }
  229.     if (!Swap || Mtasker)   {
  230.         char *cp = mxallocw(256);
  231.         sprintf(cp,"%s %s",p,command);
  232.         ret = system(cp);
  233.         xfree(cp);
  234.     } else {
  235.         ret = do_exec(p,command,USE_ALL,0xffff);
  236.     }
  237.     dos_restore(&dl);
  238.     xfree(command);
  239.     return ret;
  240. }
  241.  
  242. int
  243. dobmail(int argc,char **argv,void *p)
  244. {
  245.    int ret = _spawn("MAILER","BM.EXE",argc,argv);
  246.    /*-------------------------------------------------------------------*
  247.    * process Mailprompts                                                *
  248.    *--------------------------------------------------------------------*/
  249.    smtptick(NULL);                      /* tickle smtp to send any mail */
  250.    return ret;
  251. }
  252.  
  253. #ifdef XXX
  254. /*----------------------------------------------------------------------*
  255. * Spawn dg4dam's NNVIEW as subshell                                     *
  256. *-----------------------------------------------------------------------*/
  257. int
  258. donntpview(int argc,char **argv,void *p)
  259. {
  260.    return _spawn("NNTPVIEW","NNTPVIEW.EXE",argc,argv);
  261. }
  262. #endif
  263.  
  264. int
  265. doshell(int argc,char **argv,void *p)
  266. {
  267.    return _spawn("COMSPEC","COMMAND.COM",argc,argv);
  268. }
  269.  
  270. /* internal flags for prep_swap */
  271.  
  272. #define CREAT_TEMP      0x0080
  273. #define DONT_SWAP_ENV   0x4000
  274.  
  275. static int
  276. do_exec(char *exfn,char *epars,int spwn,unsigned needed)
  277. {
  278. char swapfn [82], execfn [82];
  279. unsigned avail;
  280. union REGS regs;
  281. int rc, swapping;
  282.  
  283.    strcpy(execfn,exfn);
  284.  
  285.    if (!spwn) {
  286.       swapping = -1;
  287.    } else {
  288.       /*----------------------------------------------------------------*
  289.       * Determine amount of free memory                                 *
  290.       *-----------------------------------------------------------------*/
  291.       regs.x.ax = 0x4800;
  292.       regs.x.bx = 0xffff;
  293.       intdos (®s, ®s);
  294.       avail = regs.x.bx;
  295.  
  296.       /*----------------------------------------------------------------*
  297.       * No swapping if available memory > needed                        *
  298.       *-----------------------------------------------------------------*/
  299.       if(needed < avail) {
  300.      swapping = 0;
  301.       } else {
  302.          /* Swapping necessary, use 'TEMP' environment variable */
  303.          /* to determine swap file path if defined. */
  304.          swapping = spwn;
  305.  
  306.          if (spwn & USE_FILE) {
  307.             char *cp;
  308.  
  309.             if((cp = getenv("TEMP")) == NULL)
  310.                 cp = "";
  311.  
  312.             strcpy(swapfn,cp);
  313.  
  314.             if (_osmajor >= 3) {
  315.                 swapping |= CREAT_TEMP;
  316.             } else {
  317.                 swapping = 0;
  318.             }
  319.          }
  320.       }
  321.    }
  322.  
  323.    /* All set up, ready to go. */
  324.  
  325.    if (swapping > 0) {
  326.       swapping |= DONT_SWAP_ENV;
  327.  
  328.       if((rc = prep_swap(swapping,swapfn)) < 0)
  329.          return RC_PREPERR | -rc;
  330.    }
  331.  
  332.    return (do_spawn(swapping,execfn,epars,0,0));
  333. }
  334.  
  335. static unsigned near
  336. dos_prepare(Doslock *dl)
  337. {
  338. #ifdef ASY
  339.    int i;
  340.    struct asy *asyp;
  341. #endif
  342.  
  343.    /*-------------------------------------------------------------------*
  344.    *  save the current screen layout                                    *
  345.    *--------------------------------------------------------------------*/
  346.    gettextinfo(&dl->tr);
  347.    dl->sbuf = mxallocw(2 * dl->tr.screenheight * dl->tr.screenwidth);
  348.    gettext(dl->tr.winleft, dl->tr.wintop,dl->tr.winright, dl->tr.winbottom,dl->sbuf);
  349.  
  350.    /*-------------------------------------------------------------------*
  351.    * temporarily suspend all our irq's                                  *
  352.    * SEE THE CHANGES IN THE PARTICULAR xxx_init() and xxx_stop rtns.    *
  353.    * dk5dc                                                              *
  354.    *--------------------------------------------------------------------*/
  355. #ifdef ASY
  356.    for(i = 0; i < ASY_MAX; i++) {       /* scan the asy structures      */
  357.       asyp = &Asy[i];
  358.       if(asyp->iface == NULLIF)
  359.          continue;
  360.       asy_stop(asyp->iface,1);          /* note:changes in 8250.c dk5dc */
  361.    }
  362. #endif
  363. #ifdef SCC
  364.    sccstop();                           /* brute force !                */
  365. #endif
  366. #ifdef PACKET
  367.    pkt_suspend();                       /* see pktdrvr.c                */
  368. #endif
  369.    stop_timer(&Statustimer);
  370.    uchtimer();                         /* disconnect the timer interrupt*/
  371.    return(1);
  372. }
  373.  
  374. /*----------------------------------------------------------------------*
  375. *-----------------------------------------------------------------------*/
  376. static void near
  377. dos_restore(Doslock *dl)
  378. {
  379. #ifdef ASY
  380. int i;
  381. #endif
  382.  
  383. #ifdef PACKET
  384. extern INTERRUPT (*Pkvec[])();
  385. #endif
  386.  
  387.    /*-------------------------------------------------------------------*
  388.    * restore the screen layout                                          *
  389.    *--------------------------------------------------------------------*/
  390.    puttext(dl->tr.winleft, dl->tr.wintop,dl->tr.winright, dl->tr.winbottom,dl->sbuf);
  391.    gotoxy(dl->tr.curx,dl->tr.cury);
  392.    xfree(dl->sbuf);
  393.  
  394.    /*-------------------------------------------------------------------*
  395.    * restore our irq's                                                  *
  396.    * SEE THE CHANGES IN THE PARTICULAR xxx_init() and xxx_stop() rtns.  *
  397.    * dk5dc                                                              *
  398.    *--------------------------------------------------------------------*/
  399. #ifdef ASY
  400.    for(i = 0; i < ASY_MAX; i++) {
  401.       struct asy *asyp = &Asy[i];
  402.  
  403.       if(asyp->iface == NULLIF) {
  404.          continue;
  405.       }
  406.       asy_init(i,asyp->iface,0,0,0,0,asyp->speed,asyp->cts,asyp->rlsd);
  407.    }
  408. #endif
  409. #ifdef SCC
  410.    sccreact();                          /* new function in scc.c dk5dc  */
  411. #endif
  412. #ifdef PACKET
  413.    pkt_restore();                                           /* see pktdrvr.c                */
  414. #endif
  415.    chtimer(btick);                      /* rechain the timer interrupt  */
  416.    start_timer(&Statustimer);
  417. }
  418.  
  419. int
  420. doswap(int argc,char **argv,void *p)
  421. {
  422.    return setbool(&Swap,"EMS/XMS/FILE swapping",argc,argv);
  423. }
  424.  
  425. /* Keyboard interrupt handler */
  426. void
  427. kbint(void)
  428. {
  429.     int c, sig = FALSE;
  430.  
  431.     while((c = kbraw()) != -1 && Keyboard.cnt < KBSIZE) {
  432.         sig = TRUE;
  433.         *Keyboard.wp++ = c;
  434.  
  435.         if(Keyboard.wp == &Keyboard.buf[KBSIZE]) {
  436.             Keyboard.wp = Keyboard.buf;
  437.         }
  438.         Keyboard.cnt++;
  439.     }
  440.     if(sig) {
  441.         psignal(&Keyboard,0);
  442.     }
  443. }
  444.  
  445. static int near
  446. kbchar(void)
  447. {
  448.     unsigned char c;
  449.     int i_state = dirps();
  450.  
  451.     while(Keyboard.cnt == 0) {
  452.         pwait(&Keyboard);
  453.     }
  454.     Keyboard.cnt--;
  455.     restore(i_state);
  456.  
  457.     c = *Keyboard.rp++;
  458.  
  459.     if(Keyboard.rp == &Keyboard.buf[KBSIZE]) {
  460.         Keyboard.rp = Keyboard.buf;
  461.     }
  462.     return c;
  463. }
  464.  
  465. /* Read characters from the keyboard, translating them to "real" ASCII.
  466.  * If none are ready, block. The F-10 key is special; translate it to -2.
  467.  */
  468. int
  469. kbread(void)
  470. {
  471.     int c;
  472.  
  473.     if((c = kbchar()) == 0){
  474.         /* Lead-in to a special char */
  475.         switch(c = kbchar()){
  476.         case 3:                                                 /* NULL (bizzare!) */
  477.             c = 0;
  478.             break;
  479.         case 83:                                                /* DEL key */
  480.             c = 0x7f;
  481.             break;
  482.         case 68:                        /* F10 - used as command escape */
  483.             c = -2;
  484.             break;
  485.         case 67:                                                /* F9 used for trace screen */
  486.             c = -13;
  487.             break;
  488.         case 80:                                                /* down */
  489.             c = 0x10;
  490.             break;
  491.         case 72:                                                /* up */
  492.             c = 0x0f;
  493.             break;
  494.         default:
  495.             if(c > 58 && c < 67) {          /* F1 to F8 */
  496.                 c = (c - 56) * -1;
  497.             } else {
  498.                 c = -1;
  499.             }
  500.         }
  501.     }
  502.     return c;
  503. }
  504.  
  505. /* Install hardware interrupt handler.
  506.  * Takes IRQ numbers from 0-7 (0-15 on AT) and maps to actual 8086/286 vectors
  507.  * Note that bus line IRQ2 maps to IRQ9 on the AT
  508.  */
  509. int
  510. setirq(unsigned irq,INTERRUPT (*handler)())
  511. {
  512.     /* Set interrupt vector */
  513.     if(irq < 8) {
  514.         setvect(8 + irq,handler);
  515.     } else if(irq < 16 && getproc() > 5) {
  516.         setvect(0x70 + irq - 8,handler);
  517.         Isat = 1;
  518.     } else {
  519.         return -1;
  520.     }
  521.     return 0;
  522. }
  523.  
  524. /* Return pointer to hardware interrupt handler.
  525.  * Takes IRQ numbers from 0-7 (0-15 on AT) and maps to actual 8086/286 vectors
  526.  */
  527. INTERRUPT
  528. (*getirq(unsigned int irq))()
  529. {
  530.     /* Set interrupt vector */
  531.     if(irq < 8){
  532.         return getvect(8+irq);
  533.     } else if(irq < 16){
  534.         return getvect(0x70 + irq - 8);
  535.     } else {
  536.         return NULLVIFP;
  537.     }
  538. }
  539.  
  540. /* Disable hardware interrupt */
  541. int
  542. maskoff(unsigned irq)
  543. {
  544.     if(irq < 8){
  545.         setbit(0x21,(1 << irq));
  546.     } else if(irq < 16){
  547.         irq -= 8;
  548.         setbit(0xa1,(1 << irq));
  549.     } else {
  550.         return -1;
  551.     }
  552.     return 0;
  553. }
  554.  
  555. /* Enable hardware interrupt */
  556. int
  557. maskon(unsigned irq)
  558. {
  559.     if(irq < 8) {
  560.         clrbit(0x21,(1 << irq));
  561.     } else if(irq < 16) {
  562.         irq -= 8;
  563.         clrbit(0xa1,(1 << irq));
  564.     } else {
  565.         return -1;
  566.     }
  567.     return 0;
  568. }
  569.  
  570. /* Return 1 if specified interrupt is enabled, 0 if not, -1 if invalid */
  571. int
  572. getmask(unsigned irq)
  573. {
  574.     if(irq < 8) {
  575.         return (inportb(0x21) & (1 << irq)) ? 0 : 1;
  576.     } else if(irq < 16) {
  577.         irq -= 8;
  578.         return (inportb(0xa1) & (1 << irq)) ? 0 : 1;
  579.     } else {
  580.         return -1;
  581.     }
  582. }
  583.  
  584. /* Called from assembler stub linked to BIOS interrupt 1C, called on each
  585.  * hardware clock tick. Signal a clock tick to the timer process.
  586.  */
  587. void
  588. ctick(void)
  589. {
  590.     Tick++;
  591.     psignal(&Tick,1);
  592. }
  593.  
  594. /* Called from the timer process on every tick. NOTE! This function
  595.  * can NOT be called at interrupt time because it calls the BIOS
  596.  */
  597. void
  598. pctick(void)
  599. {
  600.     static long oldt = 0;       /* Value of bioscnt() on last call */
  601.     static long days = 0;       /* # of times bioscnt() has rolled over */
  602.  
  603.     /* Update the time-since-boot */
  604.     long t = bioscnt();
  605.  
  606.     if(t < oldt) {
  607.         /* bioscnt has rolled past midnight */
  608.         days++;
  609.     }
  610.     oldt = t;
  611.     Clock = (days * 0x1800b0L) + t - Starttime;
  612. }
  613.  
  614. /* Set bit(s) in I/O port */
  615. void
  616. setbit(unsigned port,char bits)
  617. {
  618.     outportb(port,inportb(port)|bits);
  619. }
  620.  
  621. /* Clear bit(s) in I/O port */
  622. void
  623. clrbit(unsigned port,char bits)
  624. {
  625.     outportb(port,(inportb(port) & ~bits));
  626. }
  627.  
  628. /* Set or clear selected bits(s) in I/O port */
  629. void
  630. writebit(unsigned port,char mask,int val)
  631. {
  632.     char x = inportb(port);
  633.  
  634.     if(val) {
  635.         x |= mask;
  636.     } else {
  637.         x &= ~mask;
  638.     }
  639.     outportb(port,x);
  640. }
  641.  
  642. /* Convert a pointer to a long integer */
  643. long
  644. ptol(void *p)
  645. {
  646. #ifndef TEST
  647.     return FP_SEG(p);
  648. #else
  649.     long x = FP_OFF(p);
  650.  
  651. #ifdef  LARGEDATA
  652.     x |= (long)FP_SEG(p) << 16;
  653. #endif
  654.  
  655.     return x;
  656. #endif
  657. }
  658.  
  659. #ifdef XXX
  660. /* replaced by 'shtop' */
  661. void *
  662. ltop(long l)
  663. {
  664.     unsigned seg = (unsigned)(l >> 16);
  665.     unsigned offset = (unsigned)l;
  666.  
  667.     return (void *)MK_FP(seg,offset);
  668. }
  669. #endif
  670.  
  671. void *
  672. shtop(char *s)
  673. {
  674. #ifdef MDEBUG
  675.     return (void *)MK_FP((unsigned)htol(s),16);
  676. #else
  677.     return (void *)MK_FP((unsigned)htol(s),4);
  678. #endif
  679. }
  680.  
  681. #ifdef XXX
  682. void
  683. sysreset(void)
  684. {
  685.     void far (*foo) __ARGS((void));
  686.  
  687.     /* FFFF:0000 is hardware reset vector */
  688.     foo = MK_FP(0xffff,0);
  689.  
  690.     (*foo)();
  691. }
  692. #endif
  693.  
  694. void
  695. newscreen(struct session *sp)
  696. {
  697.     if(sp != NULLSESSION) {
  698.         sp->screen = mxallocw(sizeof(struct screen));
  699.     }
  700. }
  701.  
  702. void
  703. freescreen(struct session *sp)
  704. {
  705.     if(sp == NULLSESSION || sp->screen == NULLSCREEN) {
  706.         return;
  707.     }
  708.     if(sp->screen->save != NULLCHAR) {
  709.         xfree(sp->screen->save);
  710.     }
  711.     xfree(sp->screen);
  712.     sp->screen = NULLSCREEN;        /* finish the job */
  713. }
  714.  
  715. /* Save specified session screen and resume console screen */
  716. void
  717. swapscreen(struct session *old,struct session *new)
  718. {
  719.     struct text_info tr;
  720.     struct session *sp;
  721.     int i, size;
  722.     char *oldsave, *newsave;
  723.  
  724.     if(old == new)
  725.         return; /* Nothing to do */
  726.  
  727.     gettextinfo(&tr);
  728.     size = 2 * tr.screenheight * tr.screenwidth;
  729.  
  730.     if(old != NULLSESSION){
  731.         /* Save old screen */
  732.         if(old->screen->save == NULLCHAR)
  733.             old->screen->save = mxallocw(size);
  734.         if(old->screen->save != NULLCHAR) {
  735.             if(old->split) {
  736.                 window(1,3,80,Nrows);
  737.                 tr.winbottom = Nrows;
  738.             }
  739.             gettext(tr.winleft,tr.wintop,tr.winright,tr.winbottom,old->screen->save);
  740.         }
  741.         old->screen->row = tr.cury;
  742.         old->screen->col = tr.curx;
  743.     }
  744.     if(new != NULLSESSION){
  745.         /* Load new screen */
  746.         if(new->screen->save != NULLCHAR){
  747.             if(new->split) {
  748.                 window(1,3,80,Nrows-2);
  749.             } else {
  750.                 window(1,3,80,Nrows);
  751.                 tr.winbottom = Nrows;
  752.             }
  753.             puttext(tr.winleft,tr.wintop,tr.winright,tr.winbottom,new->screen->save);
  754.             gotoxy(new->screen->col,new->screen->row);
  755.             /* Free the memory (saves 4K on a continuous basis) */
  756.             xfree(new->screen->save);
  757.             new->screen->save = NULLCHAR;
  758.         } else {
  759.             clrscr();       /* Start with a fresh slate */
  760.             if(new->split){
  761.         new->tsavex = 1;
  762.         new->bsavex = 1;
  763.                 new->tsavey = 2;
  764.                 new->bsavey = 24;
  765.                 window(1,Nrows-1,80,Nrows);
  766.                 cputs("_\b");
  767.                 window(1,3,80,Nrows-2);
  768.             }
  769.         }
  770.     }
  771.     for(i = 0, sp = Sessions; i < Nsessions; sp++, i++) {
  772.         if(sp->type != FREE) {
  773.             if(sp->screen->save != NULLCHAR) {
  774.                 newsave = mxallocw(size);
  775.                 if(FP_SEG(newsave) < FP_SEG(sp->screen->save))   {
  776.                     memcpy(newsave,sp->screen->save,size);
  777.                     oldsave = sp->screen->save;
  778.                     sp->screen->save = newsave;
  779.                     xfree(oldsave);
  780.                 } else {
  781.                     xfree(newsave);
  782.                 }
  783.             }
  784.         }
  785.     }
  786.     alert(Display,(void *)1);       /* Wake him up */
  787. }
  788.  
  789. void
  790. display(int i,void *v1,void *v2)
  791. {
  792.     /* This is very tricky code. Because the value of "Current" can
  793.      * change any time we do a pwait, we have to be careful to detect
  794.      * any change and go back and start again.
  795.      */
  796.     for(;;){
  797.         int c;
  798.         struct session *sp = Current;
  799.  
  800.         if(sp->morewait) {
  801.             pwait(&sp->row);
  802.             if(sp != Current || sp->row <= 0) {
  803.                 /* Current changed value, or the user
  804.                  * hasn't really hit a key
  805.                  */
  806.                 continue;
  807.             }
  808.             /* Erase the prompt */
  809.             cputs("\r         \r");
  810.         }
  811.         sp->morewait = 0;
  812.  
  813.         if((c = rrecvchar(sp->output)) == -1){
  814.             /* the alert() in swapscreen will cause this to
  815.              * return -1 when current changes
  816.              */
  817.             pwait(NULL);    /* Prevent a nasty loop */
  818.             continue;
  819.         }
  820.         if(sp != Command) {
  821.             textattr(WHITE);
  822.         }
  823.         putch(c);
  824.  
  825.         textattr(LIGHTGRAY);
  826.  
  827.         if(sp->record != NULLFILE && c != '\r') {
  828.             fputc(c,sp->record);
  829.         }
  830.         if(sp->flowmode && c == '\n' && --sp->row <= 0) {
  831.             cputs("--More--");
  832.             sp->morewait = 1;
  833.         }
  834.     }
  835. }
  836.  
  837. /* Return time since startup in milliseconds. If the system has an
  838.  * 8254 clock chip (standard on ATs and up) then resolution is improved
  839.  * below 55 ms (the clock tick interval) by reading back the instantaneous
  840.  * value of the counter and combining it with the global clock tick counter.
  841.  * Otherwise 55 ms resolution is provided.
  842.  *
  843.  * Reading the 8254 is a bit tricky since a tick could occur asynchronously
  844.  * between the two reads. The tick counter is examined before and after the
  845.  * hardware counter is read. If the tick counter changes, try again.
  846.  * Note: the hardware counter counts down from 65536.
  847.  */
  848. int32
  849. msclock(void)
  850. {
  851.     int32 hi;
  852.     int16 lo, count[4];             /* extended (48-bit) counter of timer clocks */
  853.  
  854.     if(!Isat)
  855.         return Clock * MSPTICK;
  856.  
  857.     do {
  858.         hi = Clock + Tick;
  859.         lo = clockbits();
  860.     } while(hi != Clock + Tick);
  861.  
  862.     count[0] = 0;
  863.     count[1] = hi >> 16;
  864.     count[2] = hi;
  865.     count[3] = -lo;
  866.     longmul(11,4,count);    /* The ratio 11/13125 is exact */
  867.     longdiv(13125,4,count);
  868.  
  869.     return ((long)count[2] << 16) + count[3];
  870. }
  871.  
  872. /* Return clock in seconds */
  873. int32
  874. secclock(void)
  875. {
  876.     int32 hi;
  877.     int16 lo, count[4];             /* extended (48-bit) counter of timer clocks */
  878.  
  879.     if(!Isat)
  880.         return (Clock * MSPTICK) / 1000L;
  881.  
  882.     do {
  883.         hi = Clock + Tick;
  884.         lo = clockbits();
  885.     } while(hi != Clock + Tick);
  886.  
  887.     count[0] = 0;
  888.     count[1] = hi >> 16;
  889.     count[2] = hi;
  890.     count[3] = -lo;
  891.     longmul(11,4,count);    /* The ratio 11/13125 is exact */
  892.     longdiv(13125,4,count);
  893.  
  894.     longdiv(1000,4,count);  /* Convert to seconds */
  895.     return ((long)count[2] << 16) + count[3];
  896. }
  897.  
  898.